package ch.bfh.ti.barcode.io;

import java.io.IOException;
import java.io.OutputStream;

import ch.bfh.ti.barcode.exception.EncodeException;
import ch.bfh.ti.barcode.multiple.MultipleCodeGenerator;

/**
 * A simple implementation of the abstract OutputStream class writing to the
 * MultipleCodeGenerator. The implementation does not do any buffering. It just
 * writes a whole code and provides methods to write the data piece by piece.
 * 
 * @author Dominik von Burg
 * 
 */
public class CodeOutputStream extends OutputStream {
	private MultipleCodeGenerator generator;
	private byte[] buffer;
	private int bufferIndex = 0;

	public CodeOutputStream(MultipleCodeGenerator generator) {
		this.generator = generator;
		buffer = new byte[generator.getCodeSize()];
	}

	/**
	 * Writes one single byte into a buffer. As soon as the buffer is big
	 * enough, the whole content of it is being sent to the
	 * MultipleCodeGenerator. After sending the last byte, the method
	 * {@link #flush()} has to be called to make sure that the last unfilled
	 * buffer is also being sent.
	 */
	@Override
	public void write(int data) throws IOException {
		if (bufferIndex >= buffer.length)
			try {
				generator.sendData(buffer);
				bufferIndex = 0;

				// The first frame doesn't have the same size, because there is
				// a header.
				if (buffer.length != generator.getCodeSize())
					buffer = new byte[generator.getCodeSize()];
			} catch (EncodeException e) {
				throw new IOException(e.getMessage(), e);
			}

		buffer[bufferIndex] = (byte) data;
		bufferIndex++;
	}

	/**
	 * Writes a whole array of data into a buffer. As soon as the buffer is big
	 * enough, the whole content of it is being sent to the
	 * MultipleCodeGenerator. After sending the last part, the method
	 * {@link #flush()} has to be called to make sure that the last unfilled
	 * buffer is also being sent.
	 */
	@Override
	public void write(byte[] data) throws IOException {
		int writeIndex = 0;

		while (writeIndex < data.length) {
			int writeAmount = Math.min(data.length - writeIndex, buffer.length
					- bufferIndex);

			System.arraycopy(data, writeIndex, buffer, bufferIndex, writeAmount);

			writeIndex += writeAmount;
			bufferIndex += writeAmount;

			if (bufferIndex >= buffer.length)
				try {
					generator.sendData(buffer);
					bufferIndex = 0;

					// The first frame doesn't have the same size, because there
					// is a header.
					if (buffer.length != generator.getCodeSize())
						buffer = new byte[generator.getCodeSize()];
				} catch (EncodeException e) {
					throw new IOException(e.getMessage(), e);
				}
		}
	}

	/**
	 * This method sends a buffer that is not yet filled up to the
	 * MultipleCodeGenerator. The method has to be called after sending all the
	 * data to make sure that the last buffer is being sent also.
	 */
	@Override
	public void flush() throws IOException {
		if (bufferIndex == 0)
			return;

		// Clear the end of buffer the buffer.
		for (int i = bufferIndex; i < buffer.length; i++)
			buffer[i] = 0;

		try {
			generator.sendData(buffer);
			generator.flush();
		} catch (EncodeException e) {
			throw new IOException(e.getMessage(), e);
		}
	}
}